package cn.iocoder.yudao.module.trade.order.service.calculator;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.thirdparty.zxhx.dto.ZXHXSkuItemReqDTO;
import cn.iocoder.yudao.module.api.dine.vo.DineConfigVO;
import cn.iocoder.yudao.module.api.member.address.MemberAddressApi;
import cn.iocoder.yudao.module.api.member.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.api.trade.delivery.enums.DeliveryExpressChargeModeEnum;
import cn.iocoder.yudao.module.api.trade.delivery.enums.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.config.dal.dataobject.TradeConfigDO;
import cn.iocoder.yudao.module.trade.config.service.TradeConfigService;
import cn.iocoder.yudao.module.trade.delivery.bo.DeliveryExpressTemplateRespBO;
import cn.iocoder.yudao.module.trade.delivery.service.DeliveryExpressTemplateService;
import cn.iocoder.yudao.module.trade.order.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.order.bo.TradePriceCalculateRespBO;
import cn.iocoder.yudao.module.trade.order.bo.TradePriceCalculateRespBO.OrderItem;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.api.trade.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND;

/**
 * 运费的 {@link TradePriceCalculator} 实现类
 *
 * @author jason
 */
@Component
@Order(TradePriceCalculator.ORDER_DELIVERY)
@Slf4j
public class TradeDeliveryPriceCalculator implements TradePriceCalculator {

    @Resource
    private MemberAddressApi addressApi;

    @Resource
    private DeliveryExpressTemplateService deliveryExpressTemplateService;
    @Resource
    private TradeConfigService tradeConfigService;

    @Override
    public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
        if (param.getDeliveryType() == null) {
            log.debug("[calculate][配送类型/服务方式为空，不计算运费]");
            return;
        }
        if (!Arrays.asList(DeliveryTypeEnum.EXPRESS.getType(),
                        DeliveryTypeEnum.TAKE_OUT.getType())
                .contains(param.getDeliveryType())) {
            log.debug("[calculate][配送类型/服务方式为 {}，不计算运费]", param.getDeliveryType());
            return;
        }
        if (DeliveryTypeEnum.EXPRESS.getType().equals(param.getDeliveryType())) {
            // 校验是不是存在产品不能快递发货的情况。就是说，下单选择的配送类型与商品支持的配送类型不匹配
            if (CollectionUtils.anyMatch(result.getItems(), item -> !item.getDeliveryTypes().contains(param.getDeliveryType()))) {
                log.warn("[calculate][配送类型/服务方式为 {} 不被不支持，不计算运费]", param.getDeliveryType());
                return;
            }
            calculateExpress(param, result);
            log.debug("[calculate][快递运费价格计算完成] {}", result.getPrice());
            return;
        }
        if (DeliveryTypeEnum.TAKE_OUT.getType().equals(param.getDeliveryType())) {
            DineConfigVO dineConfig = result.getDineConfig();
            if (dineConfig != null) {
                log.debug("[calculate][配送类型/服务方式为 {}，通过点餐配置(满免配={}、配送费={})计算运费]", param.getDeliveryType(), dineConfig.getDeliveryFreeThresholdPrice(), dineConfig.getDeliveryPrice());
                TradePriceCalculatorHelper.recountAllPrice(result);
                if (result.getPrice().getTotalPrice() >= dineConfig.getDeliveryFreeThresholdPrice()
                    || dineConfig.getDeliveryPrice() == 0) {
                    result.setFreeDelivery(true);
                    for (OrderItem item : result.getItems()) {
                        item.setDeliveryPrice(0);
                        TradePriceCalculatorHelper.recountPayPrice(item);
                    }
                    TradePriceCalculatorHelper.recountAllPrice(result);
                } else {
                    result.setFreeDelivery(false);
                    int deliveryPrice = Math.abs(dineConfig.getDeliveryPrice());
                    divideDeliveryPrice(result.getItems(), deliveryPrice, result);
                }
            }
        }
        log.debug("[calculate][配送费价格计算完成] {}", result.getPrice());
    }

    private MemberAddressRespDTO calculateAddress(TradePriceCalculateReqBO param) {
        if (param.getAddressId() == null) {
            // 价格计算时，如果为空就不算~最终下单，会校验该字段不允许空
            log.debug("[calculateAddress][用户地址为空，不计算运费]");
            return null;
        }
        MemberAddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
        Assertions.notNull(address, "用户(" + param.getUserId() + ")的地址不能为空");
        return address;
    }

    private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
        // 0. 得到用户地址区域
        MemberAddressRespDTO address = calculateAddress(param);
        if (address == null) {
            log.debug("[calculateExpress][用户地址为空，不计算运费]");
            return;
        }
        // 情况一：全局包邮
        if (isGlobalExpressFree(result)) {
            log.debug("[calculateExpress][全局包邮，不计算运费]");
            return;
        }
        // 情况二：活动包邮
        if (Boolean.TRUE.equals(result.getFreeDelivery())) {
            log.debug("[calculateExpress][活动包邮，不计算运费]");
            return;
        }
        // 情况三：快递模版
        // 2.1 过滤出已选中的产品 SKU
        List<OrderItem> selectedItem = filterList(result.getItems(), OrderItem::getSelected);
        // 第三方供应链产品邮费查询
        List<ZXHXSkuItemReqDTO> supplyRelatedSkuItems = filterZXHXPostage(selectedItem);
        Set<Long> deliveryTemplateIds = convertSet(selectedItem.stream().filter(item -> item.getSupplyRelatedSkuId() == null || item.getSupplyRelatedSkuId() <= 0 || item.getDeliveryTemplateId() == null || item.getDeliveryTemplateId() <= 0).toList(), OrderItem::getDeliveryTemplateId);
        if (CollUtil.isEmpty(deliveryTemplateIds)) {
            log.warn("[calculateExpress][排除第三方供应链接口后({})的快递模版为空，不计算运费]", supplyRelatedSkuItems);
            return;
        }
        Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap =
                deliveryExpressTemplateService.getExpressTemplateMapByIdsAndArea(deliveryTemplateIds, address.getAreaId());
        // 2.2 计算配送费用
        if (CollUtil.isEmpty(expressTemplateMap)) {
            log.error("[calculateExpress][找不到产品 templateIds {} areaId{} 对应的运费模板]", deliveryTemplateIds, address.getAreaId());
            throw exception(PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND);
        }
        calculateDeliveryPrice(selectedItem, expressTemplateMap, result);
    }

    /**
     * 是否全局包邮
     *
     * @param result 计算结果
     * @return 是否包邮
     */
    private boolean isGlobalExpressFree(TradePriceCalculateRespBO result) {
        TradeConfigDO config = tradeConfigService.getOrCreateTradeConfigByShopId(result.getShopId());
        // 情况一：交易中心配置不存在则默认包邮
        if (config == null) {
            log.info("[calculate][店铺({}) 的交易中心配置不存在，默认包邮]", result.getShopId());
            return true;
        }
        // 情况二：开启了全局包邮 && 满足包邮金额
        return Boolean.TRUE.equals(config.getDeliveryExpressFreeEnabled()) &&
               result.getPrice().getPayPrice() >= config.getDeliveryExpressFreePrice();
    }

    private void calculateDeliveryPrice(List<OrderItem> selectedSkus,
                                        Map<Long, DeliveryExpressTemplateRespBO> expressTemplateMap,
                                        TradePriceCalculateRespBO result) {
        // 按产品运费模板来计算产品的运费：相同的运费模板可能对应多条订单产品 SKU
        Map<Long, List<OrderItem>> template2ItemMap = convertMultiMap(selectedSkus, OrderItem::getDeliveryTemplateId);
        // 依次计算快递运费
        for (Map.Entry<Long, List<OrderItem>> entry : template2ItemMap.entrySet()) {
            Long templateId = entry.getKey();
            List<OrderItem> orderItems = entry.getValue();
            DeliveryExpressTemplateRespBO templateBO = expressTemplateMap.get(templateId);
            if (templateBO == null) {
                log.error("[calculateDeliveryPrice][不能计算快递运费，找不到 templateId({}) 对应的运费模板配置]", templateId);
                continue;
            }
            // 1. 优先判断是否包邮。如果包邮不计算快递运费
            if (isExpressTemplateFree(orderItems, templateBO.getChargeMode(), templateBO.getFree())) {
                log.error("[calculateDeliveryPrice][不能计算快递运费，因为 templateId({}) 包邮]", templateId);
                continue;
            }
            // 2. 计算快递运费
            calculateExpressFeeByChargeMode(orderItems, templateBO.getChargeMode(), templateBO.getCharge());
        }
        TradePriceCalculatorHelper.recountAllPrice(result);
    }

    /**
     * 按配送类型/服务方式来计算运费
     *
     * @param orderItems     SKU 产品项目
     * @param chargeMode     配送计费方式
     * @param templateCharge 快递运费配置
     */
    private void calculateExpressFeeByChargeMode(List<OrderItem> orderItems, Integer chargeMode,
                                                 DeliveryExpressTemplateRespBO.Charge templateCharge) {
        if (templateCharge == null) {
            log.error("[calculateExpressFeeByChargeMode][计算快递运费时，找不到 SKU({}) 对应的运费模版]", orderItems);
            return;
        }
        double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
        // 1. 计算 SKU 产品快递费用
        int deliveryPrice;
        if (totalChargeValue <= templateCharge.getStartCount()) {
            deliveryPrice = templateCharge.getStartPrice();
        } else {
            double remainWeight = totalChargeValue - templateCharge.getStartCount();
            // 剩余重量/ 续件 = 续件的次数. 向上取整
            int extraNum = (int) Math.ceil(remainWeight / templateCharge.getExtraCount());
            int extraPrice = templateCharge.getExtraPrice() * extraNum;
            deliveryPrice = templateCharge.getStartPrice() + extraPrice;
        }
        // 2. 分摊快递费用到 SKU. 退费的时候，可能按照 SKU 考虑退费金额
        int remainPrice = deliveryPrice;
        for (int i = 0; i < orderItems.size(); i++) {
            TradePriceCalculateRespBO.OrderItem item = orderItems.get(i);
            int partPrice;
            double chargeValue = getChargeValue(item, chargeMode);
            if (i < orderItems.size() - 1) { // 减一的原因，是因为拆分时，如果按照比例，可能会出现.所以最后一个，使用反减
                partPrice = (int) (deliveryPrice * (chargeValue / totalChargeValue));
                remainPrice -= partPrice;
            } else {
                partPrice = remainPrice;
            }
            Assert.isTrue(partPrice >= 0, "运费分摊金额必须大于等于 0");
            // 更新快递运费
            item.setDeliveryPrice(partPrice);
            TradePriceCalculatorHelper.recountPayPrice(item);
        }
    }

    /**
     * 检查是否包邮
     *
     * @param chargeMode   配送计费方式
     * @param templateFree 包邮配置
     */
    private boolean isExpressTemplateFree(List<OrderItem> orderItems, Integer chargeMode,
                                          DeliveryExpressTemplateRespBO.Free templateFree) {
        if (templateFree == null) {
            return false;
        }
        double totalChargeValue = getTotalChargeValue(orderItems, chargeMode);
        double totalPrice = TradePriceCalculatorHelper.calculateTotalPayPrice(orderItems);
        return totalChargeValue >= templateFree.getFreeCount() && totalPrice >= templateFree.getFreePrice();
    }

    private double getTotalChargeValue(List<OrderItem> orderItems, Integer chargeMode) {
        double total = 0;
        for (OrderItem orderItem : orderItems) {
            total += getChargeValue(orderItem, chargeMode);
        }
        return total;
    }

    private double getChargeValue(OrderItem orderItem, Integer chargeMode) {
        DeliveryExpressChargeModeEnum chargeModeEnum = DeliveryExpressChargeModeEnum.valueOf(chargeMode);
        return switch (chargeModeEnum) {
            case COUNT -> orderItem.getCount();
            case WEIGHT -> orderItem.getWeight() != null ? orderItem.getWeight() * orderItem.getCount() : 0;
            case VOLUME -> orderItem.getVolume() != null ? orderItem.getVolume() * orderItem.getCount() : 0;
        };
    }

    private List<ZXHXSkuItemReqDTO> filterZXHXPostage(List<OrderItem> selectedItem) {
        List<ZXHXSkuItemReqDTO> supplyRelatedSkuItems = new ArrayList<>();
        for (OrderItem item : selectedItem) {
            if (item.getSupplyRelatedSkuId() != null && item.getSupplyRelatedSkuId() > 0) {
                ZXHXSkuItemReqDTO skuItemReqDTO = new ZXHXSkuItemReqDTO();
                skuItemReqDTO.setSkuId(item.getSupplyRelatedSkuId());
                skuItemReqDTO.setSkuNum(item.getCount());
                supplyRelatedSkuItems.add(skuItemReqDTO);
            }
        }
        if (CollUtil.isNotEmpty(supplyRelatedSkuItems)) {
            log.debug("[filterZXHXPostage][通过第三方供应链接口计算快递运费({})]", supplyRelatedSkuItems);
            // TODO 后续需要根据业务需求重构，看如何处理第三方供应链的运费
            /*ZXHXOrderPostageReqDTO postageReqDTO = new ZXHXOrderPostageReqDTO();
            postageReqDTO.setShipAreaCode(formatShipAreaCode(address.getCountyCode()));
            postageReqDTO.setSkuList(supplyRelatedSkuItems);
            ZXHXOrderPostageRespDTO postageRespDTO = ZXHXApiClientFactory.tradeClient().checkPostage(postageReqDTO);;*/
        }
        return supplyRelatedSkuItems;
    }

    private void divideDeliveryPrice(List<OrderItem> items, int deliveryPrice, TradePriceCalculateRespBO result) {
        List<Integer> prices = TradePriceCalculatorHelper.dividePrice(items, deliveryPrice);
        for (int i = 0, n = items.size(); i < n; i++) {
            TradePriceCalculateRespBO.OrderItem orderItem = items.get(i);
            orderItem.setDeliveryPrice(prices.get(i));
            TradePriceCalculatorHelper.recountPayPrice(orderItem);
        }
        TradePriceCalculatorHelper.recountAllPrice(result);
    }

}
